package cc.lj.internal.mobilepay.application.service;

import cc.lj.internal.core.exception.ServiceException;
import cc.lj.internal.core.utils.EntityMapTransUtils;
import cc.lj.internal.mobilepay.domain.model.vo.CiticLogDO;
import cc.lj.internal.mobilepay.infrastructure.excepion.MobilepayExceptionEnum;
import cc.lj.internal.mobilepay.interfaces.dto.PayResponseDTO;
import cc.lj.internal.mobilepay.interfaces.dto.QueryResponseDTO;
import cc.lj.internal.mobilepay.interfaces.dto.command.PayCommand;
import cc.lj.internal.mobilepay.interfaces.dto.command.PayQueryCommand;
import cc.lj.internal.mobilepay.interfaces.dto.command.ReverseCommand;
import cc.lj.internal.mobilepay.interfaces.dto.convert.PayAndQueryConvertDTO;
import lj.internal.dict.enums.EnumCiticApiType;
import lj.internal.dict.enums.EnumCiticService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * 主动扫码，统一支付 app Service
 * @author liangxifeng
 * @date 2022/11/20 17:24
 */
@Service
@Slf4j
public class PayAppService extends CiticAppService<PayCommand,PayResponseDTO>{
    //中信查询 app service
    @Autowired
    private QueryAppService queryAppService;

    //撤销订单 app service
    @Autowired
    private ReverseAppService resverseAppService;

    /**
     *
     * 1. 组装参数
     * 2.新增中信日志表数据
     * 3. 调用中信统一支付接口
     * 4. 将响应日志修改到数据中
     * 5. 循环查询，等待用户扫码支付，输入密码
     *
     * @param payCommand
     * @return 中信服务器返回数据
     */
    @Override
    public PayResponseDTO doMain(PayCommand payCommand)  {
        Map<String,String> reqMap = new HashMap<>();
        //从外部接收的参数
        reqMap.put("out_trade_no",payCommand.getOutTradeNo()); //商户订单号
        reqMap.put("auth_code",payCommand.getAuthCode()); //授权码
        reqMap.put("total_fee",String.valueOf(payCommand.getTotalFee())); //消费金额，单位：分
        //该接口其他必要参数
        reqMap.put("service", EnumCiticService.PAY.getCode());//交易类型
        reqMap.put("body", ljConfig.getProDesc()); //商品描述
        reqMap.put("mch_id", ljConfig.getMchId()); //商户号
        reqMap.put("version",ljConfig.getVersion()); //版本号
        reqMap.put("mch_create_ip",ljConfig.getOutIp());//出网ip
        reqMap.put("terminal_info",createTerminalInfo());//终端信息，经纬度等等

        /**
         * 2.新增中信日志表数据
         */
        CiticLogDO addLogDO = new CiticLogDO();
        addLogDO.setApiType(EnumCiticApiType.PAY.getCode()); //接口类型
        Integer lastId = citicLog.addLog(payCommand,reqMap,addLogDO);

        /**
         * 3. 调用中信统一支付接口
         */
        Map<String, String> resMap = reuqestCitic.request(reqMap);

        /**
         * 4. 将响应日志修改到数据中
         */
        CiticLogDO updateLogDo = new CiticLogDO();
        if(resMap.get("transaction_id") != null) { //微信,支付宝订单号(该接口特有的)
            updateLogDo.setTransactionId( resMap.get("transaction_id") );
        }
        if(resMap.get("trade_type") != null) {//交易类型
            updateLogDo.setTradeType(resMap.get("trade_type"));
        }
        citicLog.updateLog(lastId,resMap,updateLogDo);
        PayResponseDTO payResponseDTO = null;
        //业务结果为SUCCESS 代表支付支付成功
        if( resMap.get("result_code") != null && resMap.get("result_code").equalsIgnoreCase("SUCCESS") ) {
            log.info("pact_id:"+payCommand.getPactId()+",商户订单号:"+payCommand.getOutTradeNo()+",支付成功，结果map="+resMap);
            //这里直接组织返回值
            payResponseDTO = EntityMapTransUtils.mapToEntity1(PayResponseDTO.class,resMap);
            return payResponseDTO;
        }
        /**
         * 5. 等待用户输入密码，调用查询接口，循环每10秒查一次，查询3次
         */
        resMap.remove("sign"); //签名信息太长，可读性太差，这里删除掉了
        if( resMap.get("err_code") != null && resMap.get("err_code").equalsIgnoreCase("USERPAYING") ) {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(10000);
                    PayQueryCommand queryCommand = new PayQueryCommand();
                    queryCommand.setOutTradeNo(payCommand.getOutTradeNo());
                    queryCommand.setPactId(payCommand.getPactId());
                    queryCommand.setPerformType(payCommand.getPerformType());
                    //查询支付结果
                    QueryResponseDTO queryResponseDTO = queryAppService.doMain(queryCommand);
                    log.info("pact_id = " + payCommand.getPactId() + ", " +
                            "商户单号 = " + payCommand.getOutTradeNo() + "," +
                            "支付金额 = " + payCommand.getTotalFee() + "," +
                            "等待用户支付第" + (i + 1) + "次查询," +
                            "交易状态trade_state＝" + queryResponseDTO.getTrade_state());
                    //如果查询到支付成功，则跳出本次循环
                    if (queryResponseDTO.getTrade_state() != null && queryResponseDTO.getTrade_state().equalsIgnoreCase("SUCCESS")) {
                        log.info("pact_id:"+payCommand.getPactId()+",商户订单号:"+payCommand.getOutTradeNo()+",支付成功，结果map="+resMap);
                        payResponseDTO = PayAndQueryConvertDTO.INSTANCE.convert(queryResponseDTO);
                        return payResponseDTO;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    throw new ServiceException(MobilepayExceptionEnum.CITIC_PAY_THREE_QUERY_FAIL);
                }
            }
            //如果三次循环都未查询到用户支付成功，则撤销该笔订单
            Boolean resverseRes = resverse(payCommand);
            if(resverseRes) { //如果撤销订单成功
                throw new ServiceException(MobilepayExceptionEnum.CITIC_RESVERSE_SUCCESS);
            }else {
                return null;
            }
        } else {
            log.error("pact_id = "+payCommand.getPactId()+", " +
                    "商户单号 = "+payCommand.getOutTradeNo()+"," +
                    "支付金额 = "+payCommand.getTotalFee()+"," +
                    ",支付失败,支付结果:"+resMap);
            //接口调用失败
            throw new ServiceException(MobilepayExceptionEnum.CITIC_PAY_FAILED,resMap.toString());
        }
    }

    /**
     * 插销订单
     * @param payCommand
     * @return
     */
    private Boolean resverse(PayCommand payCommand) {
        ReverseCommand command = new ReverseCommand();
        //command.setPayType(payCommand.getPayType());
        command.setOutTradeNo(payCommand.getOutTradeNo());
        command.setPactId(payCommand.getPactId());
        command.setPerformType(payCommand.getPerformType());
        return resverseAppService.doMain(command);
    }
}
